{**********************************************************************
    This is part of the Music program
    Copyright (c) 2017-2018 Blue Sky Studio

    This unit provides custom functions and procedures for Music

    这是Music程序的一部分
    版权所有 (c) 2017年碧空工作室 

    本单元为Music提供自定义函数与过程
    utf8 版
**********************************************************************}
unit KLrcDownload;
{$mode objFPC}
{$codepage utf8}
interface

type
	LrcInfo=record
			singer:UTF8String;
			song:UTF8String;
			filename:UTF8String;
			_320hash:UTF8String;
			sqhash:UTF8String;
                        album_id:UTF8String;
			hash:UTF8String;
		end;

	LrcInfoList=array of LrcInfo;
	//UTF8String = UTF8String;

	//搜索歌词 (歌名)
	function searchLrc(song : string ):LrcInfoList;

	//搜索歌词 (歌名, 艺术家)
	function searchLrc(song, singer : string):LrcInfoList;

	//下载歌词( 网址 )
	function downLrc(url:string):UTF8String;

	//下载歌词( 网址,保存地址 )
	function downLrc(url:string;path:UTF8String):UTF8String;

	//下载歌词 (LrcInfo)
	function downLrc(l:LrcInfo):UTF8String;

	//下载歌词 (LrcInfo)
	function downLrc(l:LrcInfo; path:UTF8String):UTF8String;
        
        //下载封面
        procedure downSongImg(l:LrcInfo;path:string);

	//释放歌词信息列表
	procedure freeLrcInfoList(x:LrcInfoList);

	//print();
	procedure print(l:LrcInfo);

	//println();
	procedure println(l:LrcInfo);

	function AnsiToWide(const S: UTF8String): WideString;
	function WideToUTF8(const WS: WideString): UTF8String;
	function AnsiToUTF8(const S: UTF8String): UTF8String;
	function UTF8ToWide(const US: UTF8String): WideString;
	function WideToAnsi(const WS: WideString): UTF8String;
	function UTF8ToAnsi(const S: UTF8String): UTF8String;

	//下歌
	procedure downSong(l:LrcInfo;path:string);
	
	//下歌
	procedure downSong(url,path:string);

implementation
uses fphttpclient,SysUtils, fpjson, jsonparser, windows;


	procedure saveFile(lrctext,path:UTF8String);forward;

	function URLEncode(const S: string; const InQueryString: Boolean): string;forward;
	function httpGet(url:string):UTF8String; forward;

	function AnsiToWide(const S: UTF8String): WideString;
	var len: integer;
	ws: WideString;
	begin
	Result:='';
	if (Length(S) = 0) then
	exit;
	len:=MultiByteToWideChar(CP_ACP, 0, PChar(s), -1, nil, 0);
	SetLength(ws, len);
	MultiByteToWideChar(CP_ACP, 0, PChar(s), -1, PWideChar(ws), len);
	Result:=ws;
	end;

	function WideToUTF8(const WS: WideString): UTF8String;
	var len: integer;
	us: UTF8String;
	begin
	Result:='';
	if (Length(WS) = 0) then
	exit;
	len:=WideCharToMultiByte(CP_UTF8, 0, PWideChar(WS), -1, nil, 0, nil, nil);
	SetLength(us, len);
	WideCharToMultiByte(CP_UTF8, 0, PWideChar(WS), -1, PChar(us), len, nil, nil);
	Result:=us;
	end;

	function AnsiToUTF8(const S: UTF8String): UTF8String;
	begin
	Result:=WideToUTF8(AnsiToWide(S));
	end;

	function UTF8ToWide(const US: UTF8String): WideString;
	var len: integer;
	ws: WideString;
	begin
	Result:='';
	if (Length(US) = 0) then
	exit;
	len:=MultiByteToWideChar(CP_UTF8, 0, PChar(US), -1, nil, 0);
	SetLength(ws, len);
	MultiByteToWideChar(CP_UTF8, 0, PChar(US), -1, PWideChar(ws), len);
	Result:=ws;
	end;

	function WideToAnsi(const WS: WideString): UTF8String;
	var len: integer;
		s: UTF8String;
	begin
		Result:='';
		if (Length(WS) = 0) then exit;
		len:=WideCharToMultiByte(CP_ACP, 0, PWideChar(WS), -1, nil, 0, nil, nil)-1;
		SetLength(s, len);
		WideCharToMultiByte(CP_ACP, 0, PWideChar(WS), -1, PChar(s), len, nil, nil);
		Result:=s;
	end;

	function UTF8ToAnsi(const S: UTF8String): UTF8String;
	begin
		Result:=WideToAnsi(UTF8ToWide(S));
	end;


	//搜索歌词 (歌名)
	function searchLrc(song : string ):LrcInfoList;
	var
		url,s,temp:UTF8String;
		list:LrcInfoList;
		D, E: TJSONData;
		len,i:integer;
	begin
		url:='http://mobilecdn.kugou.com/api/v3/search/song?format=jsonp&keyword='+ URLEncode(UTF8Encode(song),false) +'&page=1&pagesize=20&showtype=1';
		s:=httpGet(url);
		if utf8decode(s)='错误' then
			setLength(list,0)
		else
		begin
			s:=copy(s,2,length(s)-2);
			D:=GetJSON(s);
			E:=D.FindPath('data');
			D:=E.FindPath('info');
			len:=D.count;
			setLength(list, len);
			for i:=0 to len-1 do
			begin
				e:=D.Items[i];
				
				temp:=E.FindPath('singername').AsJSON;
				list[i].singer:=copy(temp,2,length(temp)-2);

				temp:=E.FindPath('songname').AsJSON;
				list[i].song:=copy(temp,2,length(temp)-2);

				temp:=E.FindPath('filename').AsJSON;
				list[i].filename:=copy(temp,2,length(temp)-2);

				temp:=E.FindPath('320hash').AsJSON;
				list[i]._320hash:=copy(temp,2,length(temp)-2);

				temp:=E.FindPath('sqhash').AsJSON;
				list[i].sqhash:=copy(temp,2,length(temp)-2);

                                temp:=E.FindPath('album_id').AsJSON;
				list[i].album_id:=copy(temp,2,length(temp)-2);

				temp:=E.FindPath('hash').AsJSON;
				list[i].hash:=copy(temp,2,length(temp)-2);
			end;
		end;			
		exit(list);
	end;

	//搜索歌词 (歌名, 艺术家)
	function searchLrc(song, singer : string):LrcInfoList;
	begin
		exit(searchLrc(song+' '+singer));
	end;


	//下载歌词( 网址 )
	function downLrc(url:string):UTF8String;
	var
		s:UTF8String;
	begin
		s := httpGet(url);
		exit(s);
	end;

	//下载歌词( 网址,保存地址 )
	function downLrc(url:string;path:UTF8String):UTF8String;
	var
		s:UTF8String;
	begin
		s:=downLrc(url);
		saveFile(s,path);
		exit(s);
	end;

	//下载歌词 (LrcInfo)
	function downLrc(l:LrcInfo):UTF8String;
	var
		s,hash,url:UTF8String;
		D, E: TJSONData;
	begin
		if length(l._320hash) =32 then
			hash:=l._320hash
		else if length(l.sqhash) = 32 then
			hash:=l.sqhash
		else if length(l.hash) =32 then
			hash:=l.hash
		else begin hash:=''; s:='错误'; exit(s); end;
		url:='http://www.kugou.com/yy/index.php?r=play/getdata&hash='+hash;
		s:=httpGet(url);
		if utf8decode(s)='错误' then
			exit(s)
		else
		begin
			D:=GetJSON(s);
			if D.findPath('err_code').AsJson='0' then
			begin
				E:=D.findPath('data');
				s:=E.findPath('lyrics').AsJson;
				s:=copy(s,2,length(s)-2);
				
				s:=StringReplace(s,'\r\n',chr(13)+chr(10),[rfReplaceAll]);
			end
			else
				exit('错误');
		end;
		exit(s);
	end;

	//下载歌词 (LrcInfo)
	function downLrc(l:LrcInfo; path:UTF8String):UTF8String;
	var
		s:UTF8String;
	begin
		s:=downLrc(l);
		saveFile(s,path);
		exit(s);
	end;


	//下歌
	procedure downSong(l:LrcInfo;path:string);
	var
		s,hash,url:UTF8String;
		D, E: TJSONData;
	begin
		if length(l._320hash) =32 then
			hash:=l._320hash
		else if length(l.sqhash) = 32 then
			hash:=l.sqhash
		else if length(l.hash) =32 then
			hash:=l.hash
		else begin hash:=''; s:='错误'; end;
		url:='http://www.kugou.com/yy/index.php?r=play/getdata&hash='+hash;
		s:=httpGet(url);
		if utf8decode(s)='错误' then
			exit
		else
		begin
			D:=GetJSON(s);
			if D.findPath('err_code').AsJson='0' then
			begin
				E:=D.findPath('data');
				s:=E.findPath('play_url').AsJson;
				s:=copy(s,2,length(s)-2);
				
				s:=StringReplace(s,'\/','/',[rfReplaceAll]);
				downSong(s,path);
			end
			else
				exit;
		end;
		exit;
	end;
	
	//下歌
	procedure downSong(url,path:string);
	var
		s:UTF8String;
	begin
		s:=httpGet(url);
		saveFile(s,path);
	end;


	//释放歌词信息列表
	procedure freeLrcInfoList(x:LrcInfoList);
	begin
		setLength(x,0);
		x:=nil;
	end;

	//pint();
	procedure print(l:LrcInfo);
	var
		temp:UTF8String;
	begin
		system.write('{"singer" : "');
		temp:=(l.singer);
		system.write(temp);
		system.write('", "song" : "');
		temp:=(l.song);
		system.write(temp);
		system.write('", "filename" : "');
		temp:=(l.filename);
		system.write(temp);
		system.write('", "320hash" : "');
		temp:=(l._320hash);
		system.write(temp);
		system.write('", "sqhash" : "');
		temp:=(l.sqhash);
		system.write(temp);
		

                system.write('", "album_id" : "');
                temp:=(l.album_id);
                system.write(temp);

                system.write('", "hash" : "');
		temp:=(l.hash);
		system.write(temp);
		system.write('"}');
	end;

	//println();
	procedure println(l:LrcInfo);
	begin
		print(l);
		system.writeln;
	end;

	//URLEncode();
	function URLEncode(const S: string; const InQueryString: Boolean): string;
	var
	  Idx: Integer; // loops thru characters in string
	  MyResult:string;
	begin
	  MyResult := '';
	  for Idx := 1 to Length(S) do
	  begin
	    case S[Idx] of
	      'A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.':
		MyResult := MyResult + S[Idx];
	      ' ':
		if InQueryString then
		  MyResult := MyResult + '+'
		else
		  MyResult := MyResult + '%20';
	      else
		MyResult := MyResult + '%' + SysUtils.IntToHex(Ord(S[Idx]), 2);
	    end;
	  end;
	  exit(MyResult);
	end;

	function httpGet(url:string):UTF8String;
	var
		s:UTF8String;
	begin
		try
			s:=TFPCustomHTTPClient.SimpleGet(url);
		except
			s:='错误';
		end;
		exit(s);
	end;

	procedure saveFile(lrctext,path:UTF8String);
	var
		outfile: Text;
	begin
		assign(outfile,path);
		rewrite(outfile);
		writeln(outfile,lrctext);
		close(outfile);
	end;

        //下歌封面
	procedure downSongImg(l:LrcInfo;path:string);
	var
		s,hash,url:UTF8String;
		D, E: TJSONData;
	begin
		if length(l._320hash) =32 then
			hash:=l._320hash
		else if length(l.sqhash) = 32 then
			hash:=l.sqhash
		else if length(l.hash) =32 then
			hash:=l.hash
		else begin hash:=''; s:='错误'; end;
		url:='http://www.kugou.com/yy/index.php?r=play/getdata&hash='+hash+'&album_id='+l.album_id;
		s:=httpGet(url);
		if utf8decode(s)='错误' then
			exit
		else
		begin
			D:=GetJSON(s);
			if D.findPath('err_code').AsJson='0' then
			begin
				E:=D.findPath('data');
				s:=E.findPath('img').AsJson;
				s:=copy(s,2,length(s)-2);
				
				s:=StringReplace(s,'\/','/',[rfReplaceAll]);
				downSong(s,path);
			end
			else
				exit;
		end;
		exit;
	end;
end.